#include "Core.h"
#include "resource.h"

#include "Misc.h"
#include "Revisions.h"
#include "Mixer.h"
#include "Post.h"

#pragma comment(linker, \
    "\"/manifestdependency:type='Win32' "\
    "name='Microsoft.Windows.Common-Controls' "\
    "version='6.0.0.0' "\
    "processorArchitecture='*' "\
    "publicKeyToken='6595b64144ccf1df' "\
    "language='*'\"")

//
// Program Variables
//
HINSTANCE g_hinst;                          /// This application's HINSTANCE///
RevisionManager g_rm;

//
// Main Wnd Variables
//
const int DX_MARGIN = 50;
const int DY_MARGIN = 75;
const int DY_BOTTOM = 50;
const int DY_TRACK = 32;
const int ID_SAVE_TIMER = 6001;
const UINT SAVE_TIMER_MS = 10*1000;
bool g_modified = false;
File *g_curFile = NULL;
HWND g_hwndChild;                           /// Optional child window///
HWND g_hwndTrack;
Audio g_tick(MAKEINTRESOURCE(IDR_WAVE1));
Mixer g_mixer(g_tick);


//
// Backdrop Variables
//
vector<HWND> g_bds;


///
/// Save File
///
void SaveFile(File &file)
{
    g_modified = false;
    file.Save(g_hwndChild);
}

void SaveCurrentFile() { SaveFile(*g_curFile); }

///
/// If we have an inner child, resize it to fit.
///
void OnSize(HWND hwnd, UINT state, int cx, int cy)
{
    if (g_hwndChild) {
        MoveWindow(g_hwndChild, 
            DX_MARGIN, DY_MARGIN, 
            cx - 2*DX_MARGIN, cy - DY_MARGIN - DY_BOTTOM, TRUE);
        MoveWindow(g_hwndTrack, 
            0, 0, 
            DX_MARGIN/2, cy, TRUE);
    }
}

///
/// Creates a backdrop window for the specified monitor
///
BOOL CALLBACK CreateBackdrop(HMONITOR hmon,HDC hdcMonitor,LPRECT lprcMonitor,LPARAM dwData)
{
    MONITORINFO mi = { sizeof(mi) };
    if (!GetMonitorInfo(hmon, &mi)) return FALSE;

    HWND hwnd = CreateWindowEx(
        WS_EX_LAYERED,
        L"QwBackdropWnd",
        NULL,
        WS_POPUP | WS_VISIBLE,
        mi.rcMonitor.left,
        mi.rcMonitor.top,
        Width(mi.rcMonitor),
        Height(mi.rcMonitor),
        NULL, NULL, g_hinst, 0);
    SetLayeredWindowAttributes(hwnd, 0, 85*255/100, LWA_ALPHA);

    g_bds.push_back(hwnd);

    return TRUE;
}

void CreateBackdrops()
{
    EnumDisplayMonitors(NULL, NULL, CreateBackdrop, 0);
}

void SetCurrentFile(HWND hwnd, File &f);

///
/// Applications will typically override this and maybe even
/// create a child window.
///
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
    //
    // Create the editor
    //
    LoadLibrary(L"Riched20.dll");
    g_hwndChild = CreateWindowEx(
        0,
        RICHEDIT_CLASS,
        NULL,
        WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_AUTOVSCROLL|WS_VSCROLL/*|ES_AUTOHSCROLL|WS_HSCROLL*/,
        0, 0, 100, 100,
        hwnd,
        NULL, g_hinst, 0);
    HDC hdc = GetDC(hwnd);
    HFONT hfont = NewFont(L"Courier New", 12, hdc);
    SetWindowFont(g_hwndChild, hfont, FALSE);
    SendMessage(g_hwndChild, EM_SETEVENTMASK, 0, ENM_CHANGE);
    SendMessage(g_hwndChild, EM_SETWORDWRAPMODE, WBF_WORDBREAK, 0);

    //
    // Create Backdrops and move the main wnd ahead of it
    //
    CreateBackdrops();

    //
    // Create the revision tracker
    //
    g_hwndTrack = CreateWindowEx( 
        0,                             // no extended styles 
        TRACKBAR_CLASS,                // class name 
        NULL,            // title (caption) 
        WS_CHILD | WS_VISIBLE | 
        TBS_AUTOTICKS | TBS_BOTTOM | TBS_TOOLTIPS | TBS_VERT,  // style 
        0, 0, 0, 0,                       // size 
        hwnd,                       // parent window 
        NULL,             // control identifier 
        g_hinst,                       // instance 
        NULL                           // no WM_CREATE parameter 
        );
    SendMessage(g_hwndTrack, TBM_SETRANGE, 
        (WPARAM) FALSE,                   // redraw flag 
        (LPARAM) MAKELONG(0, g_rm.NumFiles() - 1));  // min. & max. positions
    SendMessage(g_hwndTrack, TBM_SETPOS, 
        (WPARAM) TRUE,                   // redraw flag 
        (LPARAM) (g_rm.NumFiles() - 1));  // pos
    
    //
    // Size up the window
    //
    TEXTMETRIC tm;
    HFONT oldfont = SelectFont(hdc, hfont);
    GetTextMetrics(hdc, &tm);
    HMONITOR hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
    MONITORINFO mi = { sizeof(mi) };
    if (!GetMonitorInfo(hmon, &mi)) return FALSE;
    const int editWidth = 26*3*tm.tmAveCharWidth;
    const int totalWidth = editWidth + 2*DX_MARGIN;
    SetWindowPos(hwnd, HWND_TOP,
        (Width(mi.rcMonitor) - totalWidth)/2, 0, 
        totalWidth, Height(mi.rcMonitor), 0);

    SelectFont(hdc, hfont);
    ReleaseDC(hwnd, hdc);

    //
    // Activate the editor for editor fun!
    //
    SetFocus(g_hwndChild);

    //
    // Display the last file
    //
    SetCurrentFile(hwnd, g_rm.FileFromIndex(g_rm.NumFiles() - 1));

    return TRUE;
}


///
/// Used to catch changes in the desired file to view
///
void SetCurrentFile(HWND hwnd, File &f)
{
    if (&f != g_curFile) {
        KillTimer(hwnd, ID_SAVE_TIMER);
        if (g_modified) {
            SaveCurrentFile();
        }
        SendMessage(g_hwndChild, EM_SETEVENTMASK, 0, 0);
        f.Open(g_hwndChild);
        SendMessage(g_hwndChild, EM_SETEVENTMASK, 0, ENM_CHANGE);
        g_curFile = &f;
        g_mixer.Play(g_tick);        
    }
}

void OnVScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos)
{
    if (hwndCtl == g_hwndTrack && (code == SB_THUMBPOSITION || code == SB_THUMBTRACK)) {
        SetCurrentFile(hwnd, g_rm.FileFromIndex(pos));
    }
}

///
/// Post a quit message because our application is over when the
/// user closes this window.
///
void OnDestroy(HWND hwnd)
{
    if (g_modified) {
        SaveFile(*g_curFile);
    }
    PostQuitMessage(0);
}

///
/// Interesting things will be painted here eventually.
///
void PaintContent(HWND hwnd, PAINTSTRUCT *pps)
{
    /*SetBkColor(pps->hdc, GetSysColor(COLOR_BTNFACE));
    RECT rct;
    GetClientRect(hwnd, &rct);
    rct.top = rct.bottom - DY_BOTTOM;
    ExtTextOut(pps->hdc, 0, 0, OPAQUE, &rct, L"", 0, NULL);*/
}

///
/// Paint the content as part of the paint cycle.
///
void OnPaint(HWND hwnd)
{
    PAINTSTRUCT ps;
    BeginPaint(hwnd, &ps);
    PaintContent(hwnd, &ps);
    EndPaint(hwnd, &ps);
}

///
/// OnPrintClient
///     Paint the content as requested by USER.
///
static void OnPrintClient(HWND hwnd, HDC hdc)
{
    PAINTSTRUCT ps;
    ps.hdc = hdc;
    GetClientRect(hwnd, &ps.rcPaint);
    PaintContent(hwnd, &ps);

}

void SendTo()
{
    GDataPost().Post(L"Testing Software", Draft);
}

///
/// Reactivates the save timer to execute a time from now
///
void ReactivateSaveTimer(HWND hwnd)
{
    KillTimer(hwnd, ID_SAVE_TIMER);
    SetTimer(hwnd, ID_SAVE_TIMER, SAVE_TIMER_MS, NULL);
}

static void OnTimer(HWND hwnd, UINT id)
{
    if (id == ID_SAVE_TIMER) {
        KillTimer(hwnd, ID_SAVE_TIMER);
        SaveCurrentFile();
    }
}

void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
    if (hwndCtl == g_hwndChild && codeNotify == EN_CHANGE) {
        g_modified = true;
        ReactivateSaveTimer(hwnd);
        g_mixer.Play(g_tick);
    }
    else {
        switch (id) {
            case ID_SENDTO: SendTo();
        }
    }
}

///
/// Window procedure
///
static LRESULT CALLBACK WndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uiMsg) {
    HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
    HANDLE_MSG(hwnd, WM_SIZE, OnSize);
    HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
    HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
    HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
    HANDLE_MSG(hwnd, WM_TIMER, OnTimer);
    HANDLE_MSG(hwnd, WM_VSCROLL, OnVScroll);
    case WM_PRINTCLIENT: OnPrintClient(hwnd, (HDC)wParam); return 0;
    }

    return DefWindowProc(hwnd, uiMsg, wParam, lParam);
}

///
/// Window procedure
///
LRESULT CALLBACK BackdropWndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc(hwnd, uiMsg, wParam, lParam);
}

BOOL InitApp()
{
    //
    // Main Wnd Class
    //
    WNDCLASS wc;
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = g_hinst;
    wc.hIcon = LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_ICON1));
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = L"QwWnd";
    if (!RegisterClass(&wc)) return FALSE;

    //
    // Backdrop Wnd Class
    //
    WNDCLASS wcb;
    wcb.style = 0;
    wcb.lpfnWndProc = BackdropWndProc;
    wcb.cbClsExtra = 0;
    wcb.cbWndExtra = 0;
    wcb.hInstance = g_hinst;
    wcb.hIcon = NULL;
    wcb.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcb.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
    wcb.lpszMenuName = NULL;
    wcb.lpszClassName = L"QwBackdropWnd";
    if (!RegisterClass(&wcb)) return FALSE;

    InitCommonControls();

    //
    // Load the revisions
    //
    g_rm.LoadRevisionsFromDir();
    

    return TRUE;
}

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                   LPSTR lpCmdLine, int nShowCmd)
{
    MSG msg;
    HWND hwnd;

    g_hinst = hinst;

    if (!InitApp()) return 0;

    if (SUCCEEDED(CoInitialize(NULL))) {

        hwnd = CreateWindow(
            L"QwWnd",        
            L"Quickwrite",   
            WS_POPUP,        
            0, 0, 200, 200,  
            NULL,            
            NULL,            
            hinst,                      
            0);                         

        ShowWindow(hwnd, nShowCmd);

        HACCEL haccel = LoadAccelerators(g_hinst, MAKEINTRESOURCE(IDR_ACCELERATOR1));

        while (GetMessage(&msg, NULL, 0, 0)) {
            if (!TranslateAccelerator( 
                hwnd,      // handle to receiving window 
                haccel,        // handle to active accelerator table 
                &msg))         // message data 
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }

        CoUninitialize();
    }

    return 0;
}
